import webpack from "webpack";
import { merge as mergeConfig } from "webpack-merge";
import webpackConfig from "./config";
import type { ConfigEnv, UserConfig, UserConfigExport, UserConfigFnPromise, UserConfigFnObject, UserConfigFn, CSSModulesOptions, CSSOptions, EnvType, WebpackConfigType } from "./types";
import fs from 'fs';
import dotenv from 'dotenv';

export type { ConfigEnv, UserConfig, UserConfigExport, UserConfigFnPromise, UserConfigFnObject, UserConfigFn, CSSModulesOptions, CSSOptions, EnvType, WebpackConfigType };
export { mergeConfig, webpack };

const commandName = process.argv.splice(2, 1)[0];

export function defineConfig(config: UserConfig): UserConfig
export function defineConfig(config: Promise<UserConfig>): Promise<UserConfig>
export function defineConfig(config: UserConfigFnObject): UserConfigFnObject
export function defineConfig(config: UserConfigFnPromise): UserConfigFnPromise
export function defineConfig(config: UserConfigFn): UserConfigFn
export function defineConfig(config: UserConfigExport): UserConfigExport
export function defineConfig(config: UserConfigExport): UserConfigExport {
  const isServe = commandName === "serve";
  const params: ConfigEnv = {
    command: isServe ? "serve" : "build",
    mode: isServe ? "development" : "production",
  };

  if (typeof config === "function") {
    const result = (config as UserConfigFn)(params);
    if (result && typeof (result as any).then === "function") {
      return (result as Promise<UserConfig>).then((conf) => {
        const { env, css, outputDir, assetsDir, envPrefix, assetsInlineLimit, sourcemap, reactRefresh, pages, base, ...rest } = conf || ({} as UserConfig);
        return mergeConfig(
          webpackConfig({ commandName, env, css, outputDir, assetsDir, envPrefix, assetsInlineLimit, sourcemap, reactRefresh, pages, base }),
          { ...rest }
        );
      });
    }
    const { env, css, outputDir, assetsDir, envPrefix, assetsInlineLimit, sourcemap, reactRefresh, pages, base, ...rest } = result as UserConfig;
    return mergeConfig(
      webpackConfig({ commandName, env, css, outputDir, assetsDir, envPrefix, assetsInlineLimit, sourcemap, reactRefresh, pages, base }),
      { ...rest }
    );
  }

  if (config && typeof (config as any).then === "function") {
    return (config as Promise<UserConfig>).then((conf) => {
      const { env, css, outputDir, assetsDir, envPrefix, assetsInlineLimit, sourcemap, reactRefresh, pages, base, ...rest } = conf || ({} as UserConfig);
      return mergeConfig(
        webpackConfig({ commandName, env, css, outputDir, assetsDir, envPrefix, assetsInlineLimit, sourcemap, reactRefresh, pages, base }),
        { ...rest }
      );
    });
  }

  const { env, css, outputDir, assetsDir, envPrefix, assetsInlineLimit, sourcemap, reactRefresh, pages, base, ...rest } = (config || ({} as UserConfig)) as UserConfig;
  return mergeConfig(
    webpackConfig({ commandName, env, css, outputDir, assetsDir, envPrefix, assetsInlineLimit, sourcemap, reactRefresh, pages, base }),
    { ...rest }
  );
};

export function loadEnv(mode: string, prefixes: string | string[] = 'APP_') {
  const files = [`.env.${mode}.local`, `.env.${mode}`, `.env.local`, `.env`];
  for (const f of files) {
    if (fs.existsSync(f)) dotenv.config({ path: f, override: true });
  }
  const prefixArr = Array.isArray(prefixes) ? prefixes : [prefixes];
  const result: Record<string, string> = {};
  for (const [k, v] of Object.entries(process.env)) {
    if (prefixArr.some(p => k.startsWith(p))) result[k] = String(v);
  }
  return result;
}